package bl.tools;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

public class LOCCParser {
	
	private int classes = -1, noPartitions;
	private String file;
	
	LOCCParser(String file) throws FileNotFoundException{
		this.file = file;
	}
	
	/**
	 * 
	 * @param args in, out, level ('c', 'm'), parent-component
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException{
		LOCCParser lp = new LOCCParser(args[0]);
		if(args[2].equals("c"))
			lp.writeClasses(args[1], args[3]);
		else if (args[2].equals("m"))
			lp.writeMethods(args[1], args[3]);	
	}
	
	public int getNoPartitions(){
		return this.noPartitions;
	}
	
	public int getClassLoc(String qualifier){
		String packageName, className, line = null;
		boolean gotPack = false;
		FileReader fr = null;
		try {
			fr = new FileReader(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		BufferedReader br = new BufferedReader(fr);
		int dot_i =  qualifier.lastIndexOf('.');
		className = qualifier.substring( dot_i +1 );
		packageName = qualifier.substring(0, dot_i );
		// find package
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line == null)
				throw new RuntimeException(packageName+"."+className + " not found!");
			if(line.equals("Package: " + packageName)){
				gotPack = true;
				continue;
			}
			if(line.indexOf("Package: ") != -1 && line.indexOf(packageName) == -1)
				gotPack = false;
			if(gotPack && line.indexOf("Class: "+ className) != -1){
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				return Integer.parseInt( line.substring(line.indexOf('(') +1, line.indexOf(')')) );
			}
		}
	}
	
	public HashMap<String, Integer> getPackageLoc() throws NumberFormatException, IOException{
		String packageName = "", line = null;
		FileReader fr = null;
		int currentLoc = 0;
		boolean samePackage = false;
		HashMap<String, Integer> locs = new HashMap<String, Integer>();
		fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);
		// find package
		while(true){
			line = br.readLine();
			if(line == null)
				break;
			if(line.indexOf("Package: ") != -1){
				samePackage = line.split("\\s")[1].equals(packageName);
				if(! samePackage )
					locs.put(packageName, currentLoc);
				else
					packageName = line.split("\\s")[1];
				while(true){
					line = br.readLine();
					if(line.indexOf("Class: ") != -1)
						currentLoc += Integer.parseInt(line.substring(line.indexOf('(') +1, line.length() -1));
					else
						break;
				}
			}
		}
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return locs;
	}
	
	public int countMethods(String qualifier){
		String packageName, className, line = null;
		int dot_i =  qualifier.lastIndexOf('.'), no = 0;
		className = qualifier.substring( dot_i +1 );
		packageName = qualifier.substring(0, dot_i);
		FileReader fr = null;
		try {
			fr = new FileReader(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		BufferedReader br = new BufferedReader(fr);
		// find package
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line == null)
				throw new RuntimeException(qualifier + " not found!");
			if(line.equals("Package: " + packageName))
				break;
		}
		// find class
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line == null) break;
			if(line.indexOf("Package: ") != -1)
				break;
			if(line.indexOf("Class: " +  className) != -1)
				no++;
		}
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return no;
	}
	
	public int getMethodLoc(String qualifier){
		String packageName, className, methodName, line = null;
		qualifier = qualifier.substring(0, qualifier.indexOf('('));
		int dot_i =  qualifier.lastIndexOf('.');
		methodName = qualifier.substring( dot_i +1, qualifier.length() );
		boolean looking4Constructor = false;
		if(methodName.equals("<init>")){
			methodName = "Constructor";
			looking4Constructor = true;
		}
		className = qualifier.substring(0, dot_i);
		dot_i =  className.lastIndexOf('.');
		packageName = className.substring(0, dot_i);
		className = className.substring(dot_i +1);
		FileReader fr = null;
		try {
			fr = new FileReader(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		BufferedReader br = new BufferedReader(fr);
		boolean gotPack = false, gotClass = false;
		
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line == null)
				throw new RuntimeException(packageName+"." + className + "." + methodName + " not found!");
			if(line.equals("Package: " + packageName)){
				gotPack = true;
				continue;
			}
			if(line.indexOf("Package: ") != -1 && line.indexOf(packageName) == -1)
				gotPack = false;
			
			if(gotPack && line.indexOf("Class: " + className) != -1){
				gotClass = true;
				continue;
			}
			if(line.indexOf("Class: ") != -1 && line.indexOf(className) == -1)
				gotClass = false;
			
			if(gotPack && gotClass && line.indexOf("Method: " + methodName) != -1){
				return Integer.parseInt( line.substring(line.indexOf('(') +1, line.indexOf(')')) );
			}
			if(looking4Constructor)
				return 0;
			
		}
	}
	
	/**
	 * As LOCC prints only method names and no parameters, we treat polymorphic signatures by
	 * storing the maximum LOC for all equal method names.
	 * @return maps signatures to the LOC, calculated by LOCC
	 * @throws IOException
	 */
	HashMap<String, Integer> getAllMethodsLOC() throws IOException{
		HashMap<String, Integer> out = new HashMap<String, Integer>();
		String packageName = null, className = null, methodName = null, line = null;
		FileReader fr = null;
		try {
			fr = new FileReader(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		BufferedReader br = new BufferedReader(fr);
		String sig; int openBrace_i;
		Integer currentLoc = new Integer(0), newLoc = new Integer(0);
		while(true){
			line = br.readLine();
			if(line == null)
				break;
			if(line.indexOf("Package: ") != -1)
				packageName = line.substring(9);
			if(line.indexOf("Class: ") != -1)
				className = line.substring(line.indexOf(':') +2, line.indexOf('(') -1);
			if(line.indexOf("Method: ") != -1 && line.indexOf("Method(s)") == -1){
				openBrace_i =  line.indexOf('(');
				methodName = line.substring(line.indexOf(':') +2, openBrace_i -1);
				if(methodName.equals("Constructor"))
					methodName = "<init>";
				sig = packageName + "." + className + "." + methodName;
				newLoc = new Integer(line.substring(openBrace_i + 1, line.length() -1));
				if(out.containsKey(sig)){
					currentLoc = out.get(sig);
					if(newLoc < currentLoc)
						newLoc = currentLoc;
				}
				out.put(sig, newLoc);
			}
		}
		return out;
	}
	
	public int getPackageLoc(String qualifier){
		String line = null;
		int loc = 0;
		classes = 0;
		FileReader fr = null;
		try {
			fr = new FileReader(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		BufferedReader br = new BufferedReader(fr);
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line.indexOf("Package: " + qualifier) != -1)
				break;
		}
		while(true){
			try {
				line = br.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(line.indexOf("Class: ") != -1){
				loc += Integer.parseInt(  line.substring(line.indexOf('(') +1, line.indexOf(')') )  );
				classes++;
			}
			if(line.indexOf("Package: ") != -1)
				break;
		}
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return loc;
	}
	
	public int getNoClasses(){
		return classes;
	}
	
	void writeClasses(String outFile, String packName, int maxPartSize) throws IOException{
		String line = null;
		boolean gotPack = false;
		FileReader fr = null;
		ArrayList<String> list = new ArrayList<String>();
		fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);
		// find package
		while(true){
			line = br.readLine();
			if(line == null) break;
			if(line.equals("Package: " + packName)){
				gotPack = true;
				continue;
			}
			if(line.indexOf("Package: ") != -1 && !line.equals("Package: " + packName))
				gotPack = false;
			if(gotPack && line.indexOf("Class: ") != -1){
				list.add( line.substring( line.indexOf(':') +2, line.indexOf('(') -1 ) );
			}
		}
		br.close();
		this.noPartitions = (int) Math.ceil( (double) list.size() / maxPartSize );
		if(this.noPartitions == 0)
			this.noPartitions = 1;
		int r = list.size() % this.noPartitions,
			partSize = list.size() / this.noPartitions,
			mods = 0;
		FileWriter fw;
		BufferedWriter bw;
		for(int i = 0; i < this.noPartitions; i++){
			fw = new FileWriter(outFile + "_" + i + ".lst");
			bw = new BufferedWriter(fw);
			int j, ind = 0;
			for(j = 0; j < partSize ; j++){
				ind = i * partSize + mods + j;
				bw.write( packName + "." + list.get(ind) + "\n");	
			}
			if(r > 0){
				bw.write( packName + "." + list.get(++ind) + "\n");
				r--; mods++;
			}
			bw.close();
		}
	}
	
	ArrayList<String> getMethods(String className) throws IOException{
		int lastDot = className.lastIndexOf('.');
		String line = null, packName = className.substring(0, lastDot);
		className = className.substring(lastDot +1);
		boolean gotClass= false, gotPack = false;
		FileReader fr = null;
		ArrayList<String> list = new ArrayList<String>();
		fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);
		// find package
		while(true){
			line = br.readLine();
			if(line == null) break;
			if(line.equals("Package: " + packName)){
				gotPack = true;
				continue;
			}
			if(line.indexOf("Package: ") != -1 && !line.equals("Package: " + packName))
				gotPack = false;
			if(gotPack && line.indexOf("Class: " + className) != -1){
				gotClass = true;
				continue;
			}
			if(line.indexOf("Class: ") != -1 && line.indexOf(className) == -1)
				gotClass = false;
			
			if(gotPack && gotClass && line.indexOf("Method: ") != -1){
				list.add( line.substring( line.indexOf(':') +2, line.indexOf('(') -1 ) );
			}
		}
		try {
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}
	
	private void writeClasses(String outFile, String className) throws IOException {
		writeMethods(outFile, className, 1);
	}
	
	void writeMethods(String outFile, String className, int maxPartSize) throws IOException{
		ArrayList<String> list = getMethods(className);
		this.noPartitions = (int) Math.ceil( (double) list.size() / maxPartSize );
		if(this.noPartitions == 0)
			this.noPartitions = 1;
		int r = list.size() % this.noPartitions,
			partSize = list.size() / this.noPartitions,
			mods = 0;
		FileWriter fw = null;
		BufferedWriter bw = null;
		for(int i = 0; i < this.noPartitions; i++){
			fw = new FileWriter(outFile + "_" + i + ".lst");
			bw = new BufferedWriter(fw);
			int j, ind = 0;
			for(j = 0; j < partSize ; j++){
				ind = i * partSize + mods + j;
				bw.write( className + "." + list.get(ind) + "\n");	
			}
			if(r > 0){
				bw.write( className + "." + list.get(++ind) + "\n");
				r--; mods++;
			}
			bw.close();
		}
	}
	
	private void writeMethods(String outFile, String className) throws IOException {
		writeMethods(outFile, className, 1);
	}
	
}
